const glMatrix = require('gl-matrix');
const vec3 = glMatrix.vec3;
const vec4 = glMatrix.vec4;
const mat4 = glMatrix.mat4;
const XYZ2lngLat = require('../util/lngLat2ThetaPhiXYZ').XYZ2lngLat;
const Line = require('../geometry/Line');

module.exports = class Scene {
    constructor(radius, canvasWidth, canvasHeight, viewMatrix, projMatrix) {
        this._radius = radius;
        this._canvasWidth = canvasWidth;
        this._canvasHeight = canvasHeight;

        this.viewMatrix = viewMatrix;
        this.projMatrix = projMatrix;

        this._reactLayer = {};
    }

    setRadius(radius) {
        this._radius = radius;
    }

    setCanvasWidthHeight(canvasWidth, canvasHeight) {
        this._canvasWidth = canvasWidth;
        this._canvasHeight = canvasHeight;
    }

    addReactLayer(id, geometries) {
        this._reactLayer[id] = geometries;
    }

    /**
     * 
     * 依据canvas坐标，计算出对应在地球上的点的三维空间世界坐标
     * 
     * @method getPickUpPosition
     * 
     * @param {Number} canvasX 目标点距canvas左上角x方向的像素值
     * @param {Number} canvasY 目标点距canvas左上角y方向的像素值
     * @param {Array} viewMatrix 当前视图矩阵
     * 
     * @return {Array} 目标点对应在地球上的世界坐标，如果目标点不在地球上，返回空数组
     */

    getPickUpPosition(canvasX, canvasY) {
        // 计算归一化坐标（NDC）
        const ndcX = 2 * canvasX / this._canvasWidth - 1;
        const ndcY = 1 - 2 * canvasY / this._canvasHeight;

        // 计算camera的modelMatrix
        const matrix = mat4.create();
        mat4.invert(matrix, this.viewMatrix); //viewMatrix是camera的modelMatrix的逆矩阵

        const cameraPositon = vec4.create();
        vec4.transformMat4(cameraPositon, [0, 0, 0, 1], matrix); //带引号的“视坐标”

        // 计算从视点到目标点的射线
        const verticeInNDC = [ndcX, ndcY, 0.499];
        const verticeInWorld = convertVerticeFromNdcToWorld(verticeInNDC, this.projMatrix, matrix);

        const pickDirection = vec3.create();
        vec3.normalize(pickDirection, [verticeInWorld[0] - cameraPositon[0], verticeInWorld[1] - cameraPositon[1], verticeInWorld[2] - cameraPositon[2]]);

        // const line = {
        //     vector: pickDirection,
        //     vertice: cameraPositon
        // };
        const line = new Line(cameraPositon.slice(0, 3), pickDirection);

        let vertices = null; // 与地球交点空间直角坐标
        let lngLat = null; // 与地球交点原始经纬度（中心点为[0, 0]）
        //pickVertice是笛卡尔空间直角坐标系中的坐标
        const pickVertices = getLineIntersectPointWithEarth(line, this._radius);
        if (pickVertices.length === 0) {
            //没有交点
            vertices = [];
            lngLat = [];
        } else if (pickVertices.length == 1) {
            //一个交点
            vertices = pickVertices;
            lngLat = XYZ2lngLat(...vertices[0], this._radius);
        } else if (pickVertices.length == 2) {
            //两个交点
            const pickVerticeA = pickVertices[0];
            const pickVerticeB = pickVertices[1];
            const lengthA = getLengthFromVerticeToVertice(cameraPositon, pickVerticeA);
            const lengthB = getLengthFromVerticeToVertice(cameraPositon, pickVerticeB);
            //将距离人眼更近的那个点放到前面
            vertices = lengthA <= lengthB ? [pickVerticeA, pickVerticeB] : [pickVerticeB, pickVerticeA];
            lngLat = XYZ2lngLat(...vertices[0], this._radius);
        }

        const realLngLat = [];
        if (pickVertices.length) {
            // 经纬度校正到当前中心点
            realLngLat[0] = lngLat[0] + 90
            realLngLat[1] = lngLat[1] + 0;
            if (realLngLat[0] > 180) realLngLat[0] = realLngLat[0] - 360;
            if (realLngLat[0] < -180) realLngLat[0] = realLngLat[0] + 360;
            if (realLngLat[1] > 90) realLngLat[1] = 180 - realLngLat[1];
            if (realLngLat[1] < -90) realLngLat[1] = -180 - realLngLat[1];
        }

        return {
            verticeInWorld: verticeInWorld,
            line: line,
            vertices: vertices,
            lngLat: realLngLat
        };
    }

    // 查询某一坐标下，是否有几何图形被选中
    queryReactableGeometries(canvasX, canvasY, layerId) {
        const result = this.getPickUpPosition(canvasX, canvasY);
        const line = result.line;

        const selectedIds = [];
        const geometries = this._reactLayer[layerId];

        // vec4 finalPosition = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
        const projViewMat = mat4.create();
        mat4.multiply(projViewMat, this.projMatrix, this.viewMatrix);
        const pvmMat = mat4.create();
        mat4.multiply(pvmMat, projViewMat, mat4.create());

        // 直线进行投影变换
        const lineAfterProjView = line.clone();
        const newVertice = vec4.create();
        vec4.transformMat4(newVertice, [...line.vertice, 1], projViewMat);
        lineAfterProjView.setVertice(newVertice.slice(0, 3));
        // const newVector = vec4.create();
        // vec4.transformMat4(newVector, [...line.vector, 1], projViewMat);
        // lineAfterProjView.setVector(newVector.slice(0, 3));

        // 投影后直线应由投影后两点重新生成，不可用之前的向量直接投影
        const newVerticeInWorld = vec4.create();
        vec4.transformMat4(newVerticeInWorld, [...result.verticeInWorld, 1], projViewMat);
        const pickDirection = vec3.create();
        vec3.normalize(pickDirection, [newVerticeInWorld[0] - newVertice[0], newVerticeInWorld[1] - newVertice[1], newVerticeInWorld[2] - newVertice[2]]);
        lineAfterProjView.setVector(pickDirection);

        // float aspect = u_Resolution.x / u_Resolution.y;
        // const aspect = gl.drawingBufferWidth / gl.drawingBufferHeight;
        const aspect = this._canvasWidth / this._canvasHeight;

        // float pixelWidthRatio = 1.0 / (u_Resolution.x * u_ProjMatrix[0][0]);
        const pixelWidthRatio = 1.0 / (this._canvasWidth * this.projMatrix[0]);

        for (let i = 0; i < geometries.length; i++) {
            const geometry = geometries[i];
            const position = geometry.coord;
            const positionAfterProjView = vec4.create();
            vec4.transformMat4(positionAfterProjView, [...position, 1], pvmMat);

            // v_Dis = -u_ViewMatrix[3][2] - gl_Position.w;
            // if(v_Dis < 0.6) {
            //    discard;
            // } 距离摄像机过远（地球背面）丢弃
            const dis = -this.viewMatrix[14] - positionAfterProjView[3];
            if (dis < 0.6) continue;
            
            // float pixelWidth = finalPosition.w * pixelWidthRatio;
            const pixelWidth = positionAfterProjView[3] * pixelWidthRatio;            

            // float h = 2.3 * pixelWidth * u_Size.y;
            // float w = 2.3 * pixelWidth * u_Size.x / aspect;
            const w = 2.3 * pixelWidth * geometry.size[0] / aspect;
            const h = 2.3 * pixelWidth * geometry.size[1];
            // float posiOffsetX = 2.3 * pixelWidth * u_Offset.x * 2.0 / aspect;
            // float posiOffsetY = 2.3 * pixelWidth * u_Offset.y * 2.0;
            const posiOffsetX = 2.3 * pixelWidth * geometry.offset[0] / aspect;
            const posiOffsetY = 2.3 * pixelWidth * geometry.offset[1];

            // vec2 offset = vec2(a_SideCoord.x * w - posiOffsetX, a_SideCoord.y * h - posiOffsetY);

            const leftBottom = [positionAfterProjView[0] - w - posiOffsetX, positionAfterProjView[1] - h - posiOffsetY, positionAfterProjView[2]];
            const rightTop = [positionAfterProjView[0] + w - posiOffsetX, positionAfterProjView[1] + h - posiOffsetY, positionAfterProjView[2]];

            const xy = lineAfterProjView.getXYByZ(positionAfterProjView[2]);

            if (xy[0] >= leftBottom[0] && xy[1] >= leftBottom[1] && xy[0] <= rightTop[0] && xy[1] <= rightTop[1]) {
                selectedIds.push(geometry.id);
            }            
        }

        return selectedIds;
    }
}

/**
 * 
 * 计算归一化坐标对应点的世界坐标
 * 
 * @method convertVerticeFromNdcToWorld
 * 
 * @param {Number} verticeInNDC 计算归一化坐标
 * @param {Number} projMatrix 当前投影矩阵
 * @param {Number} matrix camera的modelMatrix
 * 
 * @return {Array} 目标点对应在地球上的世界坐标，如果目标点不在地球上，返回空数组
 */
function convertVerticeFromNdcToWorld(verticeInNDC, projMatrix, matrix) {
    const columnNDC = [verticeInNDC[0], verticeInNDC[1], verticeInNDC[2], 1]; //NDC归一化坐标
    const inverseProj = mat4.create();
    mat4.invert(inverseProj, projMatrix); //投影矩阵的逆矩阵
    const columnCameraTemp = vec4.create();
    vec4.transformMat4(columnCameraTemp, columnNDC, inverseProj); //带引号的“视坐标”
    const cameraX = columnCameraTemp[0] / columnCameraTemp[3];
    const cameraY = columnCameraTemp[1] / columnCameraTemp[3];
    const cameraZ = columnCameraTemp[2] / columnCameraTemp[3];
    const cameraW = 1;
    const columnCamera = [cameraX, cameraY, cameraZ, cameraW]; //真实的视坐标
    const columnWorld = vec4.create();
    vec4.transformMat4(columnWorld, columnCamera, matrix); //单击点的世界坐标
    return columnWorld;
}

/**
 * 已验证正确
 * 计算某直线与地球的交点，有可能没有交点，有可能有一个交点，也有可能有两个交点
 * @param line 与地球求交的直线
 * @return {Array}
 */
function getLineIntersectPointWithEarth(line, radius) {
    const result = [];
    const vertice = line.vertice;
    // const direction = line.vector;
    // direction.normalize();
    const direction = vec3.create();
    vec3.normalize(direction, line.vector);
    const r = radius;
    const a = direction[0];
    const b = direction[1];
    const c = direction[2];
    const x0 = vertice[0];
    const y0 = vertice[1];
    const z0 = vertice[2];
    const a2 = a * a;
    const b2 = b * b;
    const c2 = c * c;
    const r2 = r * r;
    const ay0 = a * y0;
    const az0 = a * z0;
    const bx0 = b * x0;
    const bz0 = b * z0;
    const cx0 = c * x0;
    const cy0 = c * y0;
    const deltaA = ay0 * bx0 + az0 * cx0 + bz0 * cy0;
    const deltaB = ay0 * ay0 + az0 * az0 + bx0 * bx0 + bz0 * bz0 + cx0 * cx0 + cy0 * cy0;
    const deltaC = a2 + b2 + c2;
    const delta = 8 * deltaA - 4 * deltaB + 4 * r2 * deltaC;
    if (delta >= 0) {
        const t = a * x0 + b * y0 + c * z0;
        const A = a2 + b2 + c2;
        if (delta == 0) {
            const k = -t / A;
            const x = k * a + x0;
            const y = k * b + y0;
            const z = k * c + z0;
            const p = [x, y, z];
            result.push(p);
        } else if (delta > 0) {
            const sqrtDelta = Math.sqrt(delta);
            const k1 = (-2 * t + sqrtDelta) / (2 * A);
            const x1 = k1 * a + x0;
            const y1 = k1 * b + y0;
            const z1 = k1 * c + z0;
            const p1 = [x1, y1, z1];
            result.push(p1);

            const k2 = (-2 * t - sqrtDelta) / (2 * A);
            const x2 = k2 * a + x0;
            const y2 = k2 * b + y0;
            const z2 = k2 * c + z0;
            const p2 = [x2, y2, z2];
            result.push(p2);
        }
    }
    return result;
}

/**
 * 计算三维空间中两点之间的直线距离
 * @param vertice1
 * @param vertice2
 * @return {Number}
 */
function getLengthFromVerticeToVertice(vertice1, vertice2) {
    const length2 = Math.pow(vertice1[0] - vertice2[0], 2) + Math.pow(vertice1[1] - vertice2[1], 2) + Math.pow(vertice1[2] - vertice2[2], 2);
    const length = Math.sqrt(length2);
    return length;
}
